<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 14-4-1
 * Time: 上午10:43
 * 用于保存错误日志
 */

namespace logs;

use Ulue;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;
use ulue\libs\traits\TraitGetOption;

class FileLogger extends AbstractLogger
{
    public $enabled =true;
    protected $path;
    protected $name;
    protected $extra_headers;
    protected $messageType=3;

    protected $size = 2;
    public $target     = null;

    protected $autoClear= '7'; // day

    protected $lastLog;  # 最近一条log记录

    static protected $logs      = [];
    static protected $runtime   = [];

    use TraitGetOption;

    public function __construct(array $options=[])
    {
        $this->options  = $options;

        $path           = $this->getOption('path');
        $this->setPath( $path ? : __DIR__.'/temp');

        $logName        = date('Y_m').'/'.date('Y_m_d').'.log';
        $name           = $this->getOption('name');
        $this->setName( $name ?: $logName );

        $this->enabled  = (bool) $this->getOption('enabled');

        $size   = (int)$this->getOption('size');
        $this->setSize($size);
    }

    /**
     * @param string $message
     * @param array $context 上下文追踪信息
     * @return null|void
     */
    public function emergency($message, array $context = array())
    {
        parent::emergency($message, $context);

        $this->write($this->handleLogs());
    }

    public function alert($message, array $context = array())
    {
        parent::alert($message, $context);

        $this->write($this->handleLogs());
    }

    public function critical($message, array $context = array())
    {
        parent::critical($message, $context);

        $this->write($this->handleLogs());
    }

    public function error($message, array $context = array())
    {
        parent::error($message, $context);

        $this->write($this->handleLogs());
    }

    /**
     * 记录日志内容
     * @param $level
     * @param $message
     * @param array $context
     * @internal param string $type [description]
     * @return Logger
     */
    public function add($level, $message, $context = [])
    {
        return $this->log( $level, $message, $context );
    }

    /**
     * @param mixed $level
     * @param string $message
     * @param array $context 上下文追踪信息
     * @return $this|null
     */
    public function log( $level, $message, array $context = [])
    {
        if ( $level = $this->getSupportedType($level) ) {
            // 关闭日志记录
            if (!$this->enabled) {
                return true;
            }

            $record     = [
                'time'        => date('Y-m-d H:i:s'),
                'level'       => $level,
                'METHOD'      => $_SERVER['REQUEST_METHOD'],
                'REQUEST_URI' => $_SERVER['REQUEST_URI'],
                'message'     => $message
            ];

            if (isset($context['__type'])) {
                $record['type'] = $context['__type'];
                unset($context['__type']);
            }

            if (isset($context['args'])) {
                unset($context['args']);
            }

            $record['location']   = $context['location'];

            if (isset($context['callTrace'])) {
                $record['callTrace']   = $context['callTrace'];
            }

            self::$logs[] = $this->lastLog = $record;

        } else {
            \Trigger::error("添加未知类型[ $level ]的日志记录！！");
        }

        return true;
    }

    /**
     * 保存日志到日志文件
     * @return bool
     */
    public function save()
    {
        if ( !$this->enabled ) {
            return true;
        }

        $this->write($this->handleLogs());

        return true;
    }


    /**
     * 写入日志文件,当发生致命错误时，程序会死掉，所以直接在显示错误前写入信息到文件
     * @param $content
     * @param null $messageType
     * @param string $target
     * @param null $extra_headers
     * @return null|void
     */
    protected function write( $content, $messageType=null, $target='', $extra_headers=null )
    {
        if ( !$this->enabled || !$content) {
            return true;
        }

        $messageType = $messageType?: $this->messageType;

        if ( $messageType == 3 ) {
            $target = $target ?: $this->getLogFilePath();
            $logDir = dirname($target);

            LogHelper::createDir( $logDir );

            if ( !is_dir($logDir) ) {
                \Trigger::error("日志目录：{$logDir} 不存在！！可能是".dirname($logDir)."不可写！无法创建目录");
            }

            if ( !is_writeable($logDir) ) {
                \Trigger::error("日志目录：{$logDir} 不可写！！");
            }

            if ( is_file($target) && filesize($target)>$this->size*1000*1000 ) {
                @rename($target, dirname($target).'/'.time().'.log');
            }
        }

        $extra_headers = $extra_headers ? : $this->extra_headers;

        # $messageType=3 $message 被发送到位置为 $target 的文件里。
        return @error_log( $content, $messageType, $target, $extra_headers );
    }

    public function record($data)
    {
        return $this->write($data);
    }

    /**
     * runtime 系统运行流程追踪日志
     * @param $message
     * @param  string $type [description]
     * @return void [type]          [description]
     */
    public function runtime( $message, $type ='run')
    {

    }

    /**
     * 整理日志信息
     * @return string
     */
    protected function handleLogs()
    {
        if (!self::$logs) {
            return false;
        }

        $logs    = self::$logs;
        $content = '';
        $line    = "\n------------- Level: %s ----- Type: %s----- Time: %s -------------\n\n";

        foreach ($logs as $record) {
            $level    = isset($record['level']) ? $record['level'] : 'Unknown';
            $type     = isset($record['type']) ? $record['type'] : 'Unknown';
            $time     = isset($record['time']) ? $record['time'] : 'Unknown';
            $content .= sprintf($line, $level, $type, $time);
            $content .= 'REQUEST_METHOD : '.$record['METHOD']. ', REQUEST_URI : '.$record['REQUEST_URI']. PHP_EOL;
            $content .= 'Message : '.trim($record['message']) . PHP_EOL;
            $content .= 'Location : '.LogHelper::format($record['location']);
            if (isset($record['callTrace'])) {
                $content .= 'CallTrace : '. PHP_EOL.$record['callTrace'] ;//. PHP_EOL
            }
        }

        self::$logs = [];

        return PHP_EOL.trim(strip_tags($content)).PHP_EOL;
    }

    protected function getLogFilePath()
    {
        $path  = $this->path;
        $name = $this->name;

        if ( strpos($name, '.')===false ) {
            $name .= '.log';
        }

        return $path.'/'.$name;
    }

    /**
     * 全部记录
     * @return array
     */
    public function getLogs()
    {
        return self::$logs;
    }

    /**
     * 全部记录条数
     * @return int
     */
    public function count()
    {
        return count(self::$logs);
    }

    /**
     * 最近一条记录
     * @return mixed
     */
    public function getLastLog()
    {
        return $this->lastLog;
    }

    /**
     * @param null $type
     * @return array|null|string
     */
    protected function getSupportedType($type=null)
    {
        $types = [
            'emerg' => LogLevel::EMERGENCY,
            'alert' => LogLevel::ALERT,
            'crit'  => LogLevel::CRITICAL,
            'err'   => LogLevel::ERROR,
            'warn'  => LogLevel::WARNING,
            'notice' => LogLevel::NOTICE,
            'info'  => LogLevel::INFO,
            'debug' => LogLevel::DEBUG,
        ];

        if (!$type) {
            return $types;
        }

        $type = trim(strtolower($type));

        if (in_array($type, $types)) {
            return $type;
        }

        return isset($types[$type]) ? $types[$type] : null;
    }

    public function setPath($path)
    {
        if (is_string($path) && $path) {
            if ($path{0} ==='@' ) {
                $path = Ulue::getPath($path);
            }

            $this->path = rtrim($path, '/\\ ');
        }

        return $this;
    }

    public function getPath()
    {
        return $this->path;
    }

    /**
     * 日志存储格式命名
     * 放到 $this->path 目录下，可再加一层目录
     * @example date('Y_m').'/'.date('Y_m_d').'.log';
     * @param $name
     */
    public function setName($name)
    {
        if (is_string($name) && $name) {
            $this->name = trim($name, '/\\ ');
        }
    }

    public function getName()
    {
        return $this->name;
    }

    public function setSize($size)
    {
        $this->size  = ($size>0 && $size<6) ? (int)$size : 2;

        return $this;
    }

    public function getSize()
    {
        return $this->size;
    }

    /**
     * @reference yii2 yii\base\Object::__set()
     * @param $name
     * @param $value
     * @throws TheSystemException
     * @internal param $ [type] $name
     * @internal param $ [type] $value
     */
    public function __set($name,$value)
    {
        $method = 'set'.ucfirst( $name );

        if ( method_exists($this, $method) ) {
            $this->$method($value);
        } else if ( method_exists($this, 'get'.ucfirst( $name )) ) {
            throw new TheSystemException("Setting a Read-only property! ".get_class($this)."::{$name}", 'set');
        } else {
            throw new TheSystemException("Setting a Unknown property! ".get_class($this)."::{$name}", 'set');
        }
    }

    /**
     * @reference yii2 yii\base\Object::__set()
     * @param $name
     * @throws TheSystemException
     * @return mixed
     */
    public function __get($name)
    {
        $method = 'get'.ucfirst( $name );

        if (method_exists($this, $method)) {
            return $this->$method();
        }

        if (method_exists($this, 'set'.ucfirst( $name ))) {
            throw new TheSystemException("Getting a Write-only property! ".get_class($this)."::{$name}", 'get');
        }

        throw new TheSystemException("Getting a Unknown property! ".get_class($this)."::{$name}", 'get');
    }
}